package fr.xgouchet.wedding.client.presenter;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;

import fr.xgouchet.wedding.client.event.FeedbackMessageEvent;
import fr.xgouchet.wedding.client.i18n.Messages;
import fr.xgouchet.wedding.client.service.GuestsService;
import fr.xgouchet.wedding.client.view.ConfirmDialogBox;
import fr.xgouchet.wedding.shared.model.Guest;
import fr.xgouchet.wedding.shared.model.GuestDetails;

/**
 * Presents the guests list and lets one to edit/add each guest
 * 
 * @author x.gouchet
 * 
 */
public class GuestsListPresenter implements Presenter {

	/**
	 * Description of a Dashboard Display widget
	 */
	public interface Display {

		/**
		 * @return the Add button
		 */
		public HasClickHandlers getAddButton();

		/**
		 * @return the Edit button
		 */
		public HasClickHandlers getEditButton();

		/**
		 * @return the delete button
		 */
		public HasClickHandlers getDeleteButton();

		/**
		 * @param event
		 *            a mouse click event
		 * @return the corresponding clicked row
		 */
		public int getClickedRow(ClickEvent event);

		/**
		 * @return the clickable list
		 */
		public HasClickHandlers getList();

		/**
		 * @param g
		 *            the guest to display
		 */
		public void displayGuest(Guest g);

		/**
		 * @param data
		 *            the data to display
		 */
		public void setData(ArrayList<String> data);

		/**
		 * @return The display as a Widget object
		 */
		public Widget asWidget();
	}

	/**
	 * Description of a Guest Form widget
	 */
	public interface FormDisplay {

		/**
		 * @return the Save button
		 */
		public HasClickHandlers getSaveButton();

		/**
		 * @return the Cancel button
		 */
		public HasClickHandlers getCancelButton();

		/**
		 * @param g
		 *            the guest to display
		 */
		public void displayGuest(Guest g);

		/**
		 * @return the guest data
		 */
		public Guest getGuest();

		/**
		 * Starts the form
		 */
		public void startForm();

		/**
		 * Stops the form
		 */
		public void stopForm();

		/**
		 * @return The display as a Widget object
		 */
		public Widget asWidget();
	}

	/**
	 * Constructor
	 * 
	 * @param eventBus
	 *            the event bus
	 * @param display
	 *            the binded display to this presenter
	 * @param formDisplay
	 *            the binded form display to this presenter
	 */
	public GuestsListPresenter(HandlerManager eventBus, Display display,
			FormDisplay formDisplay) {
		mEventBus = eventBus;
		mDisplay = display;
		mFormDisplay = formDisplay;
		mGuests = null;
		mCurrentGuest = null;
		bind();
	}

	/**
	 * @see Presenter#go(HasWidgets)
	 */
	public void go(HasWidgets container) {
		container.clear();
		container.add(mDisplay.asWidget());
		fetchGuestsDetails();
	}

	/**
	 * Binds the presenter's widget to the event bus
	 */
	private void bind() {
		mDisplay.getList().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				int selectedRow = mDisplay.getClickedRow(event);

				if (selectedRow >= 0) { // ignore Checkbox
					String id = mGuests.get(selectedRow).getID();
					fetchGuest(id);
				}
			}
		});

		mDisplay.getAddButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doAddGuest();
			}
		});

		mDisplay.getEditButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doEditGuest();
			}
		});

		mDisplay.getDeleteButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doWarnAndDelete();
			}
		});

		mFormDisplay.getSaveButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doCheckAndSave();
			}
		});

		mFormDisplay.getCancelButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				mFormDisplay.stopForm();
			}
		});
	}

	/**
	 * Fetches the guests details and present them in the view
	 */
	private void fetchGuestsDetails() {
		GuestsService
				.getGuestsList(new AsyncCallback<ArrayList<GuestDetails>>() {
					public void onSuccess(ArrayList<GuestDetails> result) {
						mGuests = result;
						mCurrentGuest = null;
						presentList();
						presentGuest();
					}

					public void onFailure(Throwable caught) {
						mEventBus.fireEvent(new FeedbackMessageEvent(caught
								.getMessage(), true));
					}
				});
	}

	/**
	 * Fetches the guest details and present it in the view
	 * 
	 * @param id
	 *            the id of the guest to get
	 */
	private void fetchGuest(String id) {
		GuestsService.getGuest(id, new AsyncCallback<Guest>() {
			public void onSuccess(Guest result) {
				mCurrentGuest = result;
				presentGuest();
				mEventBus.fireEvent(new FeedbackMessageEvent("", false));
			}

			public void onFailure(Throwable caught) {
				mEventBus.fireEvent(new FeedbackMessageEvent(Messages.I18N
						.fetchError(), true));
			}

		});
	}

	/**
	 * Sends the list data to the display
	 */
	private void presentList() {
		ArrayList<String> data = new ArrayList<String>();

		Iterator<GuestDetails> it = mGuests.iterator();

		while (it.hasNext())
			data.add(it.next().getDisplayName());

		mDisplay.setData(data);
	}

	/**
	 * Sends the guest data to the display
	 */
	private void presentGuest() {
		mDisplay.displayGuest(mCurrentGuest);
	}

	/**
	 * Pops up the guest form
	 */
	private void doAddGuest() {
		mFormDisplay.displayGuest(new Guest());
		mFormDisplay.startForm();
	}

	/**
	 * Pops up the guest form
	 */
	private void doEditGuest() {
		mFormDisplay.displayGuest(mCurrentGuest);
		mFormDisplay.startForm();
	}

	/**
	 * Warns the user and delete if user confirms
	 */
	private void doWarnAndDelete() {
		if (mCurrentGuest == null)
			return;

		final ConfirmDialogBox box = new ConfirmDialogBox(Messages.I18N
				.confirmDeleteCaption(), Messages.I18N
				.confirmDeleteMessage(mCurrentGuest.getDisplayName()));

		box.getYesButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				box.hide(true);

				GuestsService.deleteGuest(mCurrentGuest.getID(),
						new AsyncCallback<Object>() {
							public void onSuccess(Object result) {
								fetchGuestsDetails();
							}

							public void onFailure(Throwable caught) {
								mEventBus.fireEvent(new FeedbackMessageEvent(
										Messages.I18N.saveError(), true));
							}
						});
			}
		});

		box.getNoButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				box.hide(true);
			}
		});
	}

	/**
	 * Checks the form data and save if the data is valid
	 */
	private void doCheckAndSave() {
		Guest guest = mFormDisplay.getGuest();
		String msg = "";
		boolean valid = true;

		// check names
		if (guest.getFirstName().length() == 0) {
			valid = false;
			msg += Messages.I18N.nameInvalid();
		}

		// Check age
		if (guest.getAge().length() != 0) {
			try {
				Integer.valueOf(guest.getAge());
			} catch (Exception e) {
				valid = false;
				msg += Messages.I18N.ageInvalid();
			}
		}

		if (!valid) {
			mEventBus.fireEvent(new FeedbackMessageEvent(msg, true));
			return;
		}

		mFormDisplay.stopForm();
		GuestsService.saveGuest(guest, new AsyncCallback<Object>() {
			public void onSuccess(Object result) {
				fetchGuestsDetails();
			}

			public void onFailure(Throwable caught) {
				mEventBus.fireEvent(new FeedbackMessageEvent(Messages.I18N
						.saveError(), true));
			}
		});

	}

	/**
	 * Event Bus used to handle all communication between different parts of the
	 * module
	 */
	private final HandlerManager mEventBus;

	/**
	 * The linked display (widget)
	 */
	private Display mDisplay;

	/**
	 * The linked form display
	 */
	private FormDisplay mFormDisplay;

	/** The list of guests to present */
	private ArrayList<GuestDetails> mGuests;

	/** the guest visible in the view guest */
	private Guest mCurrentGuest;
}
